{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from collections import defaultdict\n",
    "import scipy.sparse as ss\n",
    "import scipy.io as sio\n",
    "import scipy.spatial.distance as ssd\n",
    "\n",
    "from numpy.random import random\n",
    "import pickle\n",
    "import json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user</th>\n",
       "      <th>song</th>\n",
       "      <th>play_count</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOCKSGZ12A58A7CA4B</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOCVTLJ12A6310F0FD</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SODLLYS12A8C13A96B</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOEGIYH12A6D4FC0E3</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOFRQTD12A81C233C0</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                       user                song  play_count\n",
       "0  4e11f45d732f4861772b2906f81a7d384552ad12  SOCKSGZ12A58A7CA4B           1\n",
       "1  4e11f45d732f4861772b2906f81a7d384552ad12  SOCVTLJ12A6310F0FD           1\n",
       "2  4e11f45d732f4861772b2906f81a7d384552ad12  SODLLYS12A8C13A96B           3\n",
       "3  4e11f45d732f4861772b2906f81a7d384552ad12  SOEGIYH12A6D4FC0E3           1\n",
       "4  4e11f45d732f4861772b2906f81a7d384552ad12  SOFRQTD12A81C233C0           2"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dpath = ''\n",
    "df_triplet = pd.read_csv('triplet_dataset_sub.csv')\n",
    "df_triplet.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user</th>\n",
       "      <th>song</th>\n",
       "      <th>play_count</th>\n",
       "      <th>total_play_count</th>\n",
       "      <th>fractional_play_count</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOCKSGZ12A58A7CA4B</td>\n",
       "      <td>1</td>\n",
       "      <td>259</td>\n",
       "      <td>0.003861</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOCVTLJ12A6310F0FD</td>\n",
       "      <td>1</td>\n",
       "      <td>259</td>\n",
       "      <td>0.003861</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SODLLYS12A8C13A96B</td>\n",
       "      <td>3</td>\n",
       "      <td>259</td>\n",
       "      <td>0.011583</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOEGIYH12A6D4FC0E3</td>\n",
       "      <td>1</td>\n",
       "      <td>259</td>\n",
       "      <td>0.003861</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>4e11f45d732f4861772b2906f81a7d384552ad12</td>\n",
       "      <td>SOFRQTD12A81C233C0</td>\n",
       "      <td>2</td>\n",
       "      <td>259</td>\n",
       "      <td>0.007722</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                       user                song  play_count  \\\n",
       "0  4e11f45d732f4861772b2906f81a7d384552ad12  SOCKSGZ12A58A7CA4B           1   \n",
       "1  4e11f45d732f4861772b2906f81a7d384552ad12  SOCVTLJ12A6310F0FD           1   \n",
       "2  4e11f45d732f4861772b2906f81a7d384552ad12  SODLLYS12A8C13A96B           3   \n",
       "3  4e11f45d732f4861772b2906f81a7d384552ad12  SOEGIYH12A6D4FC0E3           1   \n",
       "4  4e11f45d732f4861772b2906f81a7d384552ad12  SOFRQTD12A81C233C0           2   \n",
       "\n",
       "   total_play_count  fractional_play_count  \n",
       "0               259               0.003861  \n",
       "1               259               0.003861  \n",
       "2               259               0.011583  \n",
       "3               259               0.003861  \n",
       "4               259               0.007722  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_triplet_users = df_triplet[['user','play_count']].groupby('user').sum().reset_index()\n",
    "\n",
    "df_triplet_users.rename(columns = {'play_count':'total_play_count'}, inplace = True)\n",
    "df_triplet = pd.merge(df_triplet, df_triplet_users)\n",
    "\n",
    "df_triplet['fractional_play_count'] = df_triplet['play_count']/df_triplet['total_play_count']\n",
    "\n",
    "del df_triplet_users\n",
    "df_triplet.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/anaconda3/lib/python3.7/site-packages/sklearn/model_selection/_split.py:2179: FutureWarning: From version 0.21, test_size will always complement train_size unless both are specified.\n",
      "  FutureWarning)\n"
     ]
    }
   ],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "total_index = df_triplet.index\n",
    "\n",
    "train_index, test_index = train_test_split(total_index, train_size = 0.8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_triplet_train = df_triplet.iloc[train_index]\n",
    "df_triplet_test = df_triplet.iloc[test_index]\n",
    "\n",
    "df_triplet_train.to_csv(path_or_buf = dpath + 'triplet_dataset_sub_train.csv')\n",
    "df_triplet_test.to_csv(path_or_buf = dpath + 'triplet_dataset_sub_test.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user</th>\n",
       "      <th>song</th>\n",
       "      <th>play_count</th>\n",
       "      <th>total_play_count</th>\n",
       "      <th>fractional_play_count</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>30837</th>\n",
       "      <td>b7c24f770be6b802805ac0e2106624a517643c17</td>\n",
       "      <td>SOWRREB12A6D4FA7CB</td>\n",
       "      <td>2</td>\n",
       "      <td>2040</td>\n",
       "      <td>0.000980</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23600</th>\n",
       "      <td>2c2790c0ce23f00f1b642fd356ec6854a133d083</td>\n",
       "      <td>SOHFJAQ12AB017E4AF</td>\n",
       "      <td>1</td>\n",
       "      <td>610</td>\n",
       "      <td>0.001639</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21538</th>\n",
       "      <td>35fcd4682de9c5da1291058a22910aca0bb6f106</td>\n",
       "      <td>SOZVCRW12A67ADA0B7</td>\n",
       "      <td>3</td>\n",
       "      <td>88</td>\n",
       "      <td>0.034091</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3237</th>\n",
       "      <td>4be305e02f4e72dad1b8ac78e630403543bab994</td>\n",
       "      <td>SOGSAYQ12AB018BA14</td>\n",
       "      <td>8</td>\n",
       "      <td>3868</td>\n",
       "      <td>0.002068</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21827</th>\n",
       "      <td>138d73356c23e6e12aa82fb5dc9225428c196464</td>\n",
       "      <td>SOTIDKX12A6D4FA7EA</td>\n",
       "      <td>1</td>\n",
       "      <td>361</td>\n",
       "      <td>0.002770</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                           user                song  \\\n",
       "30837  b7c24f770be6b802805ac0e2106624a517643c17  SOWRREB12A6D4FA7CB   \n",
       "23600  2c2790c0ce23f00f1b642fd356ec6854a133d083  SOHFJAQ12AB017E4AF   \n",
       "21538  35fcd4682de9c5da1291058a22910aca0bb6f106  SOZVCRW12A67ADA0B7   \n",
       "3237   4be305e02f4e72dad1b8ac78e630403543bab994  SOGSAYQ12AB018BA14   \n",
       "21827  138d73356c23e6e12aa82fb5dc9225428c196464  SOTIDKX12A6D4FA7EA   \n",
       "\n",
       "       play_count  total_play_count  fractional_play_count  \n",
       "30837           2              2040               0.000980  \n",
       "23600           1               610               0.001639  \n",
       "21538           3                88               0.034091  \n",
       "3237            8              3868               0.002068  \n",
       "21827           1               361               0.002770  "
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_triplet_train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "n_user=788, n_item=800\n",
      "(788, 800)\n"
     ]
    }
   ],
   "source": [
    "#建立倒排表\n",
    "users = list(df_triplet_train['user'].unique())\n",
    "items = list(df_triplet_train['song'].unique())\n",
    "\n",
    "n_user = len(users)\n",
    "n_item = len(items)\n",
    "\n",
    "print(\"n_user=%d, n_item=%d\" %(n_user, n_item))\n",
    "\n",
    "#倒排表\n",
    "user_items = defaultdict(set)\n",
    "item_users = defaultdict(set)\n",
    "\n",
    "user_item_scores = ss.dok_matrix((n_user, n_item))\n",
    "\n",
    "users_index = dict()\n",
    "items_index = dict()\n",
    "for i, e in enumerate(users):\n",
    "    users_index[e] = i\n",
    "\n",
    "for i, e in enumerate(items):\n",
    "    items_index[e] = i  \n",
    "    \n",
    "n_records = df_triplet_train.shape[0]\n",
    "for i in range(n_records):\n",
    "    users_index_i = users_index[df_triplet_train.iloc[i]['user']]\n",
    "    items_index_i = items_index[df_triplet_train.iloc[i]['song']]\n",
    "    \n",
    "    user_items[users_index_i].add(items_index_i) #该用户的所有歌曲\n",
    "    item_users[items_index_i].add(users_index_i) #播放歌曲的所有用户\n",
    "    \n",
    "    score = df_triplet_train.iloc[i]['fractional_play_count']\n",
    "    user_item_scores[users_index_i, items_index_i] = score\n",
    "    \n",
    "pickle.dump(user_items, open('user_items.pkl','wb'))\n",
    "pickle.dump(item_users, open('item_users.pkl','wb'))\n",
    "\n",
    "sio.mmwrite(\"user_item_scores\", user_item_scores)\n",
    "\n",
    "pickle.dump(users_index, open('users_index.pkl','wb'))\n",
    "pickle.dump(items_index, open('items_index.pkl','wb'))\n",
    "\n",
    "print(user_item_scores.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(788,)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#平均分\n",
    "users_mu = np.zeros(n_user)\n",
    "for u in range(n_user):\n",
    "    n_user_items = 0\n",
    "    r_acc = 0.0\n",
    "    \n",
    "    for i in user_items[u]: #用户打过分的items\n",
    "        r_acc += user_item_scores[u,i]\n",
    "        n_user_items += 1\n",
    "    users_mu[u] = r_acc/n_user_items\n",
    "pickle.dump(users_mu, open('users_mu.pkl','wb'))\n",
    "    \n",
    "users_mu.shape    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "#是否播放过 为特征\n",
    "def user_similarity_played(uid1, uid2):\n",
    "    \n",
    "    #播放过的\n",
    "    s1 = user_items[uid1]\n",
    "    s2 = user_items[uid2] \n",
    "\n",
    "    intersection = s1.intersection(s2)\n",
    "    \n",
    "    if(0 != intersection):\n",
    "        union = s1.union(s2)      \n",
    "        similarity = float(len(intersection))/float(len(union))\n",
    "    else:\n",
    "        similarity = 0.0\n",
    "        \n",
    "    return similarity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ui = %d 0\n",
      "ui = %d 100\n",
      "ui = %d 200\n",
      "ui = %d 300\n",
      "ui = %d 400\n",
      "ui = %d 500\n",
      "ui = %d 600\n",
      "ui = %d 700\n"
     ]
    }
   ],
   "source": [
    "#计算所有user的相似度\n",
    "user_similarity_matrix = np.matrix(np.zeros(shape=(n_user, n_user)), float)\n",
    "for ui in range(n_user):\n",
    "    user_similarity_matrix[ui,ui]=1.0\n",
    "    \n",
    "    #进度条\n",
    "    if(0 == ui%100):\n",
    "        print(\"ui = %d\", ui)\n",
    "        \n",
    "    for uj in range(ui + 1, n_user): #用户打过分的items\n",
    "        user_similarity_matrix[uj, ui] = user_similarity_played(ui, uj)\n",
    "        user_similarity_matrix[ui, uj] = user_similarity_matrix[uj, ui]   \n",
    "        \n",
    "pickle.dump(user_similarity_matrix, open('user_similarity_played.pkl','wb'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "#是否播放过为特征\n",
    "def item_similarity_played(iid1, iid2):\n",
    "    \n",
    "    #播放过的\n",
    "    s1 = item_users[iid1]\n",
    "    s2 = item_users[iid2] \n",
    "\n",
    "    intersection = s1.intersection(s2)\n",
    "    \n",
    "    if(0 != intersection):\n",
    "        union = s1.union(s2)      \n",
    "        similarity = float(len(intersection))/float(len(union))\n",
    "    else:\n",
    "        similarity = 0.0\n",
    "        \n",
    "    return similarity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "i = %d 800\n",
      "i = %d 800\n",
      "i = %d 800\n",
      "i = %d 800\n",
      "i = %d 800\n",
      "i = %d 800\n",
      "i = %d 800\n",
      "i = %d 800\n"
     ]
    }
   ],
   "source": [
    "#计算所有item的相似度\n",
    "item_similarity_matrix = np.matrix(np.zeros(shape=(n_item, n_item)), float)\n",
    "for i in range(n_item):\n",
    "    item_similarity_matrix[i,i]=1.0\n",
    "    \n",
    "    #进度条\n",
    "    if(0 == i%100):\n",
    "        print(\"i = %d\", n_item)\n",
    "        \n",
    "    for j in range(i + 1, n_user): #用户打过分的items\n",
    "        item_similarity_matrix[j, i] = item_similarity_played(i, j)\n",
    "        item_similarity_matrix[i, j] = item_similarity_matrix[j, i]   \n",
    "        \n",
    "pickle.dump(item_similarity_matrix, open('item_similarity_played.pkl','wb'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "#训练SVD模型\n",
    "K = 40\n",
    "\n",
    "#item 和用户偏置项\n",
    "bi = np.zeros((n_item, 1))\n",
    "bu = np.zeros((n_user, 1))\n",
    "\n",
    "#矩阵q p偏置项\n",
    "qi = np.zeros((n_item, K))\n",
    "pu = np.zeros((n_user, K))\n",
    "\n",
    "for uid in range(n_user):\n",
    "    pu[uid] = np.reshape(random((K,1))/10*(np.sqrt(K)), K)\n",
    "    \n",
    "for iid in range(n_item):\n",
    "    qi[iid] = np.reshape(random((K,1))/10*(np.sqrt(K)), K)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "#预测用户uid 对item（iid）的打分\n",
    "def svd_pred(uid, iid):\n",
    "    score = users_mu[uid] + bi[iid] + bu[uid] + np.sum(qi[iid] * pu[uid])\n",
    "    return score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user</th>\n",
       "      <th>song</th>\n",
       "      <th>play_count</th>\n",
       "      <th>total_play_count</th>\n",
       "      <th>fractional_play_count</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>30837</th>\n",
       "      <td>b7c24f770be6b802805ac0e2106624a517643c17</td>\n",
       "      <td>SOWRREB12A6D4FA7CB</td>\n",
       "      <td>2</td>\n",
       "      <td>2040</td>\n",
       "      <td>0.000980</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23600</th>\n",
       "      <td>2c2790c0ce23f00f1b642fd356ec6854a133d083</td>\n",
       "      <td>SOHFJAQ12AB017E4AF</td>\n",
       "      <td>1</td>\n",
       "      <td>610</td>\n",
       "      <td>0.001639</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21538</th>\n",
       "      <td>35fcd4682de9c5da1291058a22910aca0bb6f106</td>\n",
       "      <td>SOZVCRW12A67ADA0B7</td>\n",
       "      <td>3</td>\n",
       "      <td>88</td>\n",
       "      <td>0.034091</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3237</th>\n",
       "      <td>4be305e02f4e72dad1b8ac78e630403543bab994</td>\n",
       "      <td>SOGSAYQ12AB018BA14</td>\n",
       "      <td>8</td>\n",
       "      <td>3868</td>\n",
       "      <td>0.002068</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21827</th>\n",
       "      <td>138d73356c23e6e12aa82fb5dc9225428c196464</td>\n",
       "      <td>SOTIDKX12A6D4FA7EA</td>\n",
       "      <td>1</td>\n",
       "      <td>361</td>\n",
       "      <td>0.002770</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                           user                song  \\\n",
       "30837  b7c24f770be6b802805ac0e2106624a517643c17  SOWRREB12A6D4FA7CB   \n",
       "23600  2c2790c0ce23f00f1b642fd356ec6854a133d083  SOHFJAQ12AB017E4AF   \n",
       "21538  35fcd4682de9c5da1291058a22910aca0bb6f106  SOZVCRW12A67ADA0B7   \n",
       "3237   4be305e02f4e72dad1b8ac78e630403543bab994  SOGSAYQ12AB018BA14   \n",
       "21827  138d73356c23e6e12aa82fb5dc9225428c196464  SOTIDKX12A6D4FA7EA   \n",
       "\n",
       "       play_count  total_play_count  fractional_play_count  \n",
       "30837           2              2040               0.000980  \n",
       "23600           1               610               0.001639  \n",
       "21538           3                88               0.034091  \n",
       "3237            8              3868               0.002068  \n",
       "21827           1               361               0.002770  "
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_triplet_train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(30015, 5)\n",
      "step 0 is running\n",
      "the rmse : 0.05756325609855473\n",
      "step 1 is running\n",
      "the rmse : 0.05756325609855452\n",
      "step 2 is running\n",
      "the rmse : 0.05756325609855455\n",
      "step 3 is running\n",
      "the rmse : 0.05756325609855469\n",
      "step 4 is running\n",
      "the rmse : 0.05756325609855472\n",
      "step 5 is running\n",
      "the rmse : 0.057563256098554685\n",
      "step 6 is running\n",
      "the rmse : 0.057563256098554755\n",
      "step 7 is running\n",
      "the rmse : 0.05756325609855464\n",
      "step 8 is running\n",
      "the rmse : 0.057563256098554595\n",
      "step 9 is running\n",
      "the rmse : 0.05756325609855447\n",
      "step 10 is running\n",
      "the rmse : 0.05756325609855476\n",
      "step 11 is running\n",
      "the rmse : 0.05756325609855478\n",
      "step 12 is running\n",
      "the rmse : 0.05756325609855481\n",
      "step 13 is running\n",
      "the rmse : 0.05756325609855491\n",
      "step 14 is running\n",
      "the rmse : 0.057563256098554526\n",
      "step 15 is running\n",
      "the rmse : 0.0575632560985546\n",
      "step 16 is running\n",
      "the rmse : 0.05756325609855458\n",
      "step 17 is running\n",
      "the rmse : 0.05756325609855465\n",
      "step 18 is running\n",
      "the rmse : 0.05756325609855453\n",
      "step 19 is running\n",
      "the rmse : 0.057563256098554685\n",
      "step 20 is running\n",
      "the rmse : 0.057563256098554505\n",
      "step 21 is running\n",
      "the rmse : 0.05756325609855459\n",
      "step 22 is running\n",
      "the rmse : 0.057563256098554484\n",
      "step 23 is running\n",
      "the rmse : 0.057563256098554734\n",
      "step 24 is running\n",
      "the rmse : 0.057563256098554595\n",
      "step 25 is running\n",
      "the rmse : 0.0575632560985544\n",
      "step 26 is running\n",
      "the rmse : 0.05756325609855467\n",
      "step 27 is running\n",
      "the rmse : 0.05756325609855459\n",
      "step 28 is running\n",
      "the rmse : 0.05756325609855446\n",
      "step 29 is running\n",
      "the rmse : 0.057563256098554616\n",
      "step 30 is running\n",
      "the rmse : 0.05756325609855472\n",
      "step 31 is running\n",
      "the rmse : 0.057563256098554755\n",
      "step 32 is running\n",
      "the rmse : 0.05756325609855496\n",
      "step 33 is running\n",
      "the rmse : 0.05756325609855476\n",
      "step 34 is running\n",
      "the rmse : 0.05756325609855482\n",
      "step 35 is running\n",
      "the rmse : 0.057563256098554505\n",
      "step 36 is running\n",
      "the rmse : 0.057563256098554776\n",
      "step 37 is running\n",
      "the rmse : 0.05756325609855457\n",
      "step 38 is running\n",
      "the rmse : 0.05756325609855472\n",
      "step 39 is running\n",
      "the rmse : 0.057563256098554595\n",
      "step 40 is running\n",
      "the rmse : 0.05756325609855457\n",
      "step 41 is running\n",
      "the rmse : 0.057563256098554824\n",
      "step 42 is running\n",
      "the rmse : 0.057563256098554484\n",
      "step 43 is running\n",
      "the rmse : 0.05756325609855462\n",
      "step 44 is running\n",
      "the rmse : 0.05756325609855441\n",
      "step 45 is running\n",
      "the rmse : 0.057563256098554595\n",
      "step 46 is running\n",
      "the rmse : 0.057563256098554734\n",
      "step 47 is running\n",
      "the rmse : 0.05756325609855464\n",
      "step 48 is running\n",
      "the rmse : 0.05756325609855477\n",
      "step 49 is running\n",
      "the rmse : 0.057563256098554644\n"
     ]
    }
   ],
   "source": [
    "#svd模型训练\n",
    "print(df_triplet_train.shape)\n",
    "steps = 50\n",
    "gamma = 0.04\n",
    "Lambda = 0.15\n",
    "\n",
    "#总的打分数目\n",
    "n_records = df_triplet_train.shape[0]\n",
    "for step in range(steps):\n",
    "    print('step %d is running'%step)\n",
    "\n",
    "    rmse_sum = 0.0\n",
    "    \n",
    "    #打撒样本\n",
    "    kk = np.random.permutation(n_records)\n",
    "    \n",
    "    for j in range(n_records):\n",
    "        #每次训练一个样本\n",
    "        \n",
    "        line = kk[j]\n",
    "        uid = users_index[df_triplet_train.iloc[line]['user']]\n",
    "        iid = items_index[df_triplet_train.iloc[line]['song']]\n",
    "        \n",
    "        rating = df_triplet_train.iloc[line]['fractional_play_count']\n",
    "\n",
    "        #预测残差\n",
    "        eui = rating - svd_pred(uid, iid)\n",
    "        eui = rating - score\n",
    "\n",
    "        rmse_sum += eui**2\n",
    "        \n",
    "        #随机梯度下降\n",
    "        bu[uid] += gamma * (eui - Lambda * bu[uid])\n",
    "        bi[iid] += gamma * (eui - Lambda * bi[iid])\n",
    "        \n",
    "        temp = qi[iid]\n",
    "        qi[iid] += gamma * (eui*pu[uid] - Lambda * qi[iid])\n",
    "        pu[uid] += gamma * (eui*temp - Lambda * pu[uid])\n",
    "      \n",
    "    #学习率下降\n",
    "    gamma = gamma * 0.93 \n",
    "    print('the rmse :', np.sqrt(rmse_sum/n_records))\n",
    "        \n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "#保存模型参数\n",
    "def save_json(filepath):\n",
    "    dict_ = {}\n",
    "    dict_['mu'] = users_mu.tolist()\n",
    "    \n",
    "    dict_['K'] = K\n",
    "    \n",
    "    dict_['bi'] = bi.tolist()\n",
    "    dict_['bu'] = bu.tolist()\n",
    "    dict_['qi'] = qi.tolist()\n",
    "    dict_['pu'] = pu.tolist()\n",
    "    \n",
    "    json_txt = json.dumps(dict_)\n",
    "    \n",
    "    with open(filepath,'w') as file:\n",
    "        file.write(json_txt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "save_json('svd_model.json')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
